package com.nrgedge.magnolia.servlet;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
import com.yahoo.platform.yui.compressor.JavaScriptCompressor;

public abstract class AbstractHoganPrecompileServlet extends HttpServlet {

	private static final long serialVersionUID = -1198787740246208751L;

	protected final Logger log = LoggerFactory.getLogger(getClass());

	public abstract InputStream getHoganLibrary(final HttpServletRequest request);

	public abstract Map<String, String> getTemplates(final HttpServletRequest request);

	@Override
	protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {

		final ScriptEngine engine = new ScriptEngineManager().getEngineByName("ECMAScript");

		final Map<String, String> compiled = new LinkedHashMap<String, String>();
		engine.put("compiled", compiled);

		final Reader hoganReader = new InputStreamReader(getHoganLibrary(request));

		final Map<String, String> templates = getTemplates(request);

		try {
			engine.eval(hoganReader);
			
			final HtmlCompressor htmlCompressor = new HtmlCompressor();

			for (final String templateName : templates.keySet()) {
				// compress the html before compiling into hogan function
				final String compressedTemplate = htmlCompressor.compress(templates.get(templateName));
				
				engine.put(templateName, compressedTemplate);

				final StringBuilder script = new StringBuilder();
				script.append("compiled.put(\"").append(templateName).append("\", Hogan.compile(").append(templateName).append(", {asString: true}));");
				engine.eval(script.toString());

			}

		} catch (ScriptException e) {
			this.log.error("ScriptException in doGet()", e);
		}

		//final PrintWriter writer = response.getWriter();
		//writer.write("if (!!!templates) var templates = {};");

		final StringBuilder builder = new StringBuilder();
		builder.append("if (!!!templates) var templates = {};");
		
		for (final String key : compiled.keySet()) {
			//final StringBuilder builder = new StringBuilder();
			// remove unnecessary whitespace lines
			String function = compiled.get(key);
			function = function.replace("_.b(\"\\n\" + i);", "");
			function = function.replace("_.b(\"\\n\");", "");
			
			builder.append("\n\ntemplates[\"").append(key).append("\"] = new Hogan.Template(").append(function).append(");");
			//builder.append("\n\n");
			//writer.write(builder.toString());
		}
		
		//response.setHeader("Cache-Control", "n");
		//TODO set actual modified date
		//response.setHeader("Last-Modified", "Wed, 14 Aug 2013 20:29:41 GMT");
		response.setContentType("application/javascript; charset=utf-8");// add charset
		response.setHeader("Vary", "Accept-Encoding");
	
		final InputStream stream = new ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
		InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
		
		final JavaScriptCompressor compressor = new JavaScriptCompressor(reader, new ErrorReporter() {

            public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) {
                if (line < 0) {
                    System.err.println("\n[WARNING] " + message);
                } else {
                    System.err.println("\n[WARNING] " + line + ':' + lineOffset + ':' + message);
                }
            }

            public void error(String message, String sourceName, int line, String lineSource, int lineOffset) {
                if (line < 0) {
                    System.err.println("\n[ERROR] " + message);
                } else {
                    System.err.println("\n[ERROR] " + line + ':' + lineOffset + ':' + message);
                }
            }

            public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) {
                error(message, sourceName, line, lineSource, lineOffset);
                return new EvaluatorException(message);
            }
        });

        // Close the input stream first, and then open the output stream,
        // in case the output file should override the input file.
        reader.close(); reader = null;

        compressor.compress(response.getWriter(), 0, false, false, false, false);
		
	}

}
